{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f7dd5dbd-6981-4ee6-9f1a-16789c830b63",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DatasetDict({\n",
      "    train: Dataset({\n",
      "        features: ['content', 'summary'],\n",
      "        num_rows: 114599\n",
      "    })\n",
      "    validation: Dataset({\n",
      "        features: ['content', 'summary'],\n",
      "        num_rows: 1070\n",
      "    })\n",
      "})\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>content</th>\n",
       "      <th>summary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>类型#裙*材质#绸缎*图案#印花*裙长#连衣裙*裙领型#圆领</td>\n",
       "      <td>小圆领的设计，修饰你的脖颈气质满满。大面积的印花设计提升整体视觉效果，绸缎面的设计亮眼又吸睛，是你值得拥有的一款连衣裙哦。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>类型#上衣*风格#高贵*风格#性感*图案#蝴蝶结*衣样式#衬衫*衣款式#拼接</td>\n",
       "      <td>双色拼接而出的典雅时尚，对比强烈的网格花纹，吸睛夺目，演绎别致性感魅力。立体的衬衫领，结合俏皮的蝴蝶结萦绕&lt;UNK&gt;，优雅又不失甜美气质。斜边扣开叉裙摆，时尚大气展露美腿魅惑，经典的x字版型，勾勒出凹凸有致的曼妙身姿，让你举手&lt;UNK&gt;间尽显高贵名媛气质。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>类型#上衣*版型#显瘦*材质#羊绒*颜色#紫色*风格#复古*图案#复古*衣样式#开衫*衣门襟#一粒扣*衣款式#亮片</td>\n",
       "      <td>整身&lt;UNK&gt;饱和的粉紫色为基调，会让人联想到浪漫的薰衣草，外搭的开衫，简单的在门襟点缀一粒扣，渲染出复古风情，背心连身裙，融入亮片装饰，多了一些前卫和时髦的视觉效果。包臀的版型和抹胸的领口，展示十足女性魅力，与上身开衫结合在一起，能修饰女性身形，显高显瘦，打造出明亮摩登的视觉效果。羊绒面料制作，让质感提升。</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>input_ids</th>\n",
       "      <th>labels</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>[64790, 64792, 30910, 33467, 31010, 49534, 30998, 37505, 31010, 41260, 30998, 37505, 31010, 32502, 30998, 37505, 31010, 45859, 30998, 55500, 46025, 31010, 55233, 55500, 30998, 55500, 54847, 54888, 31010, 55740, 54847, 30910, 34372, 54835, 45578, 41260, 45859, 42128, 54534, 55233, 55500, 54530, 56896, 54815, 54542, 54578, 56164, 54807, 31123, 41398, 55846, 39298, 42128, 31827, 31123, 32428, 54541, 32425, 51130, 37539, 33286, 33870, 54706, 31155, 55740, 54847, 34481, 40697, 33894, 31123, 51605, 54657, 55101, 33550, 37003, 54530, 54888, 55033, 35642, 31155, 2]</td>\n",
       "      <td>[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, 30910, 34372, 54835, 45578, 41260, 45859, 42128, 54534, 55233, 55500, 54530, 56896, 54815, 54542, 54578, 56164, 54807, 31123, 41398, 55846, 39298, 42128, 31827, 31123, 32428, 54541, 32425, 51130, 37539, 33286, 33870, 54706, 31155, 55740, 54847, 34481, 40697, 33894, 31123, 51605, 54657, 55101, 33550, 37003, 54530, 54888, 55033, 35642, 31155, 2]</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f325ff7adfaf4658bd4f2a35f99719fe",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Loading checkpoint shards:   0%|          | 0/7 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n",
      "You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.\n",
      "No label_names provided for model class `PeftModelForCausalLM`. Since `PeftModel` hides base models input arguments, if label_names is not given, label_names can't be set automatically within `Trainer`. Note that empty label_names list will be used instead.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前模型占用显存（单位为MiB）： 3739.6914672851562\n",
      "3739.69MiB\n",
      "trainable params: 974,848 || all params: 6,244,558,848 || trainable%: 0.0156\n",
      "开始训练...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/dist-packages/torch/_dynamo/eval_frame.py:838: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.5 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  return fn(*args, **kwargs)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='100' max='100' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [100/100 47:57, Epoch 0/1]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Step</th>\n",
       "      <th>Training Loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>10</td>\n",
       "      <td>4.667800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>20</td>\n",
       "      <td>3.793500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>30</td>\n",
       "      <td>3.607000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>40</td>\n",
       "      <td>3.509600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>50</td>\n",
       "      <td>3.447400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>60</td>\n",
       "      <td>3.413400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>70</td>\n",
       "      <td>3.371300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>80</td>\n",
       "      <td>3.379500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>90</td>\n",
       "      <td>3.377200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>100</td>\n",
       "      <td>3.366300</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table><p>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/dist-packages/torch/_dynamo/eval_frame.py:838: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.5 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  return fn(*args, **kwargs)\n",
      "/usr/local/lib/python3.10/dist-packages/torch/_dynamo/eval_frame.py:838: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.5 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  return fn(*args, **kwargs)\n",
      "/usr/local/lib/python3.10/dist-packages/torch/_dynamo/eval_frame.py:838: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.5 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  return fn(*args, **kwargs)\n",
      "/usr/local/lib/python3.10/dist-packages/torch/_dynamo/eval_frame.py:838: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.5 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.\n",
      "  return fn(*args, **kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "模型保存完成！\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1IAAAIoCAYAAABj6NoUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAq8tJREFUeJzs3Xd8FHX6B/DP7KZCGqElQCgJSAQSELAgICBNDzkL6k9AKZa7U892eip3omA5QNTT886GBeyFo5yoFIGoKJwgojFAKAETCSGShBRI3ZnfH3O7yabvk83OzObzfr32xWZ2dvf7/cwm5MnMPKNomqaBiIiIiIiIms1m9ACIiIiIiIishoUUERERERGRh1hIEREREREReYiFFBERERERkYdYSBEREREREXmIhRQREREREZGHWEgRERERERF5iIUUERERERGRh1hIEREREREReYiFFBERuSiKgrFjx7boNVJSUqAoChYsWOCVMREREZkRCykiIpNRFMWjGzWtd+/eCAkJMXoYXnHs2DHMmzcPQ4cORVRUFIKCghAbG4spU6Zg+fLlqKioMHqIRERtQoDRAyAiInePPPJInWXPPvssCgsL633Mm/bt24d27dq16DXOO+887Nu3D506dfLSqMjpvffew0033YTS0lIMGzYM119/PSIjI5GTk4MtW7Zg7ty5eOutt7B582ajh0pE5PcUTdM0owdBRESN6927N37++WfwR7ZM7969kZOTg7KyMqOHIrZ+/XpMmTIFUVFReP/99zFx4kS3xzVNw5o1a/Dqq6/ik08+MWiURERtBw/tIyKyqKNHj0JRFMyZMwf79u3DlVdeiY4dO0JRFBw9ehQAsHr1akyfPh19+/ZFu3btEBkZidGjR+Pf//53va9Z3zlSc+bMgaIoOHLkCP7xj38gMTERwcHB6NWrFxYuXAhVVd3Wb+gcqd69e6N3794oKSnBXXfdhW7duiE4OBjJyclYuXJlg3P8v//7P0RHRyMsLAxjxozBl19+iQULFkBRFKSkpEiia9Tp06fxyCOPIDExESEhIYiOjsaUKVPw9ddf11m3rKwMTz/9NAYPHozIyEi0b98evXv3xrXXXosffvjBtZ6qqnj11Vdx3nnnITo6GqGhoejRowemTp3arDk4HA7cfvvtUFUVH374YZ0iCtC33ZVXXolVq1a5ljWW0/Lly6EoCpYvX+5a1thnau/evQgPD0dCQkKD40xOTkZoaCiKiopcyzRNw+uvv46RI0ciIiIC7dq1w/Dhw/H66683OW8iIjPjoX1ERBZ36NAhXHDBBUhKSsKcOXOQl5eHoKAgAMC8efMQFBSEUaNGITY2Fr/++iv+85//4Oqrr8Y//vEP3HHHHc1+nz//+c/44osvcNlll2Hy5MlYs2YNFixYgIqKCjzxxBPNeo3KykpMmjQJBQUFmDZtGs6cOYP3338f1157LdavX49Jkya51j127BguvPBCHD9+HJdccgnOOeccpKenY+LEibj44os9C6mZysrKcPHFF+Pbb7/F0KFDcffdd+PEiRP44IMPsGHDBrz33nu45pprXOvPnj0bH374IZKTkzF37lwEBwcjKysLW7duxc6dOzF48GAA+nZ48sknkZCQgBkzZiA8PBzHjh3Dtm3b8PnnnzfZ4GPr1q3IyMjAhRdeiPHjxze6bnBwcItzqO8zFRUVhWnTpmHFihX45ptvcOGFF7o954cffkBqair+7//+DxEREQD0ImrmzJl477330K9fP8yYMQNBQUHYtGkTbrrpJuzduxdPPfVUi8dLRGQIjYiITK9Xr15a7R/ZR44c0QBoALSHH3643ucdPny4zrLi4mItKSlJi4yM1E6fPu32GABtzJgxbstmz56tAdD69OmjZWdnu5b/+uuvWlRUlBYeHq6Vl5e7lm/dulUDoD3yyCP1zuHyyy93W//zzz/XAGiTJ092W//666/XAGhPPPGE2/LXXnvNNe+tW7fWO+/aevXqpQUHBze53sKFCzUA2syZMzVVVV3Ld+/erQUFBWlRUVFaUVGRpmmadurUKU1RFG3YsGFaVVWV2+tUVVVpBQUFrq+jo6O1bt261clb0zQtLy+vyXEtWLBAA6A99NBDTa5b0yOPPNJgTm+88YYGQHvjjTdcy5r6TDm31a233lrnsXvvvVcDoK1bt8617JVXXtEAaHPnztUqKipcy8vLy7WpU6dqALRdu3Z5NCciIrPgoX1ERBYXExODv/71r/U+Fh8fX2dZWFgY5syZg8LCQuzcubPZ7zN//nzExsa6vu7UqRMuv/xyFBcXIz09vdmv8/e//921xwwAxo8fj169ermNpby8HB999BG6dOmCe++91+35c+fORf/+/Zv9fp5YsWIFAgMDsXjxYreOiOeccw5mz56NU6dOYc2aNQD0Q+k0TUNISAhsNvf/Tu12O6KiotyWBQUFwW6313nP6OjoJseVk5MDAOjRo4eHM5Jp6DM1btw4dO/eHR9++CEqKytdy1VVxbvvvovOnTtj8uTJruX//Oc/0b59e/zrX/9CYGCga3lQUJBrL+Z7773XijMhImo9PLSPiMjiBg8e7FaY1JSbm4vFixfjs88+w88//4zS0lK3x7Ozs5v9PsOGDauzzPmL/alTp5r1GlFRUejTp0+9r7N9+3bX1+np6SgvL8fw4cPrHKqmKAouvPBCj4q35igqKkJGRgbOPvvseguWcePGYdmyZdizZw9uuOEGRERE4De/+Q0+/fRTDB06FNdccw3Gjh2Lc889161oAIDrrrsOL7zwAgYNGoTrrrsO48aNw4gRIxAaGurVOXhLQ58pm82GmTNn4sknn8Snn36Kyy+/HACwefNmHD9+HHfccQcCAvRfLc6cOYPU1FR069YNS5YsqfNazkJs//79rTgTIqLWw0KKiMjiunbtWu/y/Px8nHvuucjMzMTIkSMxYcIEREVFwW63Y8+ePVi7di3Ky8ub/T7O815qcv7S7HA4mvUakZGR9S4PCAhwa1rhbFbQpUuXetdvaM4t4XzPhl7buTeuZiOFjz76CH/729/w7rvvuvbgREREYO7cufjb3/7maiX/3HPPoU+fPnjjjTfw+OOP4/HHH0dISAiuvfZaPP300022io+JiQGgnzfmC43le8MNN+DJJ5/E22+/7Sqk3nrrLddjTgUFBdA0DceOHcPChQsbfL3Tp097adRERL7FQ/uIiCyuoYvyvvbaa8jMzMRjjz2Gbdu24fnnn8djjz2GBQsW4IILLvDxKD3jLNpyc3PrffzEiROt9p4Nvbbz8LqaBWW7du3w+OOPIyMjAxkZGXjttdfQv39/PPfcc7jnnntc6wUEBOC+++5DWloajh07hnfffRejR4/Gm2++iZkzZzY5tpEjRwKAx9eHch5yWFVVVeexwsLCBp/X2IWeBw0ahCFDhmDdunUoLCzEmTNnsHr1avTv3x/nnnuuaz1nTsOGDYOmaQ3etm7d6tGciIjMgoUUEZGfOnz4MAC49hrU9NVXX/l6OB7p378/goOD8d1339XZa6ZpmtthgN4SERGB+Ph4HDp0qN49P84W4kOGDKn3+X369MGNN96IL774AmFhYfjPf/5T73rdunXD9OnTsX79evTt2xeff/55nUMuaxs3bhzi4+PxzTffNFl41MyrQ4cOAOrfk/X99983+jqNueGGG1BWVoaVK1di9erVKCkpwfXXX++2Tnh4OM4++2zs27ev2Yd+EhFZCQspIiI/1atXLwDAtm3b3Ja/++67+PTTT40YUrMFBwfj6quvxokTJ/Dss8+6Pfbmm2+22nk1s2fPRmVlJebNm+d28eMff/wRy5cvR2RkJK644goAwK+//oqffvqpzmsUFBSgvLwcISEhAPTC5ptvvqmz3unTp1FSUoLAwMA6zSpqs9vt+Ne//gWbzYZrr70WW7ZsqXe9jz/+GFdffbXra+ceojfffNPt0Mnt27fjnXfeafQ9GzNjxgzY7Xa89dZbeOutt6AoSp1CCgDuvPNOnDlzBrfccku9h/AdOXLEdc0zIiKr4TlSRER+6oYbbsCSJUtwxx13YOvWrejVqxd++OEHbN68GVdddZXbhVvNaNGiRfj888/x4IMP4osvvnBdR2rdunW45JJLsH79+iYLkJoqKysxZ86cBh9fvnw57r//fnzyySd46623sG/fPowfPx65ubn44IMPUFVVhWXLliE8PByAvpfnnHPOweDBg5GcnIzu3bsjLy8Pa9euRWVlJe677z4AQGlpKUaOHImzzjoLw4YNQ8+ePVFSUoJ169YhJycH9913X7Ou/XTJJZfgrbfews0334zx48dj+PDhGDFiBMLDw3HixAmkpKTg8OHDmDBhgus5F1xwAUaOHIktW7ZgxIgRuOiii/Dzzz9j7dq1mDp1KlavXt3s/GqKiYnBhAkTsHHjRthsNowaNQq9e/eus97vf/977NixAytWrMDXX3+NCRMmoFu3bjhx4gT279+P//73v3j33XfrfS4RkdmxkCIi8lM9evTAF198gfvvvx+ff/45qqqqMHToUGzcuBFZWVmmL6Ti4uKwfft2PPDAA9i4cSO++OILDBs2DBs3bsRHH30EoP4GGA1RVRUrVqxo8PHly5cjJCQEW7ZswZIlS/DBBx/g73//O9q1a4cxY8bgL3/5C0aNGuVav3fv3liwYAG2bNmCzz//HHl5eejUqROGDh2Ku+66C5dccgkAoH379liyZAk2b96Mr776Crm5uejQoQP69++PRYsW4brrrmv2HGbMmIExY8bg+eefx8aNG7FixQqcOXMG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KeDevW/jRh8OxkLqy42axtxkmJscs5NhbjLMTabZhcCpU8C0aXqhVE8zCzeqqq83bZr+PB/o3bs3RowYgffee8+17LPPPkNhYSGuc5430UzXXnstiouLsWjRIlx77bVYvnx5vefdS4qo5cuX49prr4XdbseiRYtwyy23YNWqVRg1apRbsTJt2jSsXr0ac+fOxQsvvIA777wTxcXFyMzMBADk5uZi0qRJOHr0KB588EE8//zzmDlzJnbs2OH2fvPmzcPZZ59dp1BrSFVVFU6ePIns7Gxs3LgRDz30EMLDw3Heeec16/kVFRU4efIksrKysHr1ajz11FPo1asX+vbt61pny5YtSExMxKeffooePXogPDwcHTt2xPz58912mmzbtg1lZWXo27cvrr76arRr1w6hoaEYOXIk9uzZ06zxeIupmk00JTMz0+04zLi4OGzYsAH33HMPkpOT0b17d9x111144IEHDBxl65g8GXjsMf3+hg3A3LnGjoeIiIgIK1YAZ87U3QvVEFXV13/zTeDOO1t3bP8zY8YMzJs3D6WlpQgNDcU777yDMWPGoFu3bh69zjnnnIPXXnvN9XVeXh5ee+01LFmypEXjq6ysxAMPPIBBgwbhyy+/dJ2vNmrUKFx22WX4+9//joULF+LUqVP45ptvsHTpUtx3332u59fcyfDNN9+goKAAGzduxPDhw13Lm+qE3ZRdu3ZhxIgRrq/79++P//znP4iOjm7W81etWoXp06e7vh4+fDhef/11BARUlyKHDx+G3W7H3Llzcf/992Pw4MFYtWoVHn/8cVRVVWHRokUAgIMHDwLQ552QkIA333wThYWFWLhwIS6++GJX4zFfMM0eqfqkpKS4NZpISUnB8uXL3dYZMWIEduzYgbKyMhw+fBh/+ctfGmx9bmXnnw9ERur3N20CeJQCERERGUrTgOeflz33H/9ofvHVQtdeey1KS0uxbt06FBcXY926dQ0e1tcYZxdAp9GjRyMvL891KR2pXbt2ITc3F7fddptb048pU6YgMTERn3zyCQC9M15QUBBSUlLcOsbWFBUVBQBYt25do4ftLV++HJqmuQ6xa8qAAQOwadMmrFmzBvfffz/at2/vUXe7cePGYdOmTfjoo4/whz/8AYGBgTh9+rTbOiUlJSgoKMDChQvx6KOPYtq0aXjnnXdwySWX4LnnnnM163C+r6Io2Lx5M2bMmIFbb70Va9asQUFBAf71r381e1wtZepCqi2y2WyIj4+v0wElIAAYP16/n58PfPedAYMzsYZyo8YxNxnmJsfsZJibDHOTCw4ObnqlvDzg8GHPCyJN059XozV8a+rcuTMmTJiAd999F6tWrYLD4cDVV1/t8ev07NnT7WtnN8jaRU2zsqvh559/BqDv5aktMTHR9XhwcDCWLFmCzz77DF27dsVFF12EJ5980q31/pgxYzBt2jQsXLgQnTp1wuWXX4433nijxecQRUREYMKECbj88suxZMkS3Hvvvbj88svxww8/NOv5Xbt2xYQJE3D11VfjxRdfxGWXXYaJEye6jT00NBQA3PZcOb8uLS3F999/77be1KlTXe3XAeCCCy5Anz598M0337Rorp7gTxaTURQFERERbu1GnWqeJ+XD8zQtobHcqGHMTYa5yTE7GeYmw9xknNexbDI3D/ZI1KuJduDeNGPGDHz22Wd46aWXcOmll7r23HiioSOeap4T1ezshO6++24cOHAAixYtQkhICObPn4+zzz7bVWQoioKVK1di+/bt+OMf/4hjx47hxhtvxLBhwzzag9SUq666CoB+DViJq6++GiUlJVi7dq1r3M5DLbt27eq2bpcuXQBUF6wNredct6G9da2BhZTJOBwOpKam1tthiIVUwxrLjRrG3GSYmxyzk2FuMsxNRtM0nDlzpummCTX2BoiEh7fs+R648sorYbPZsGPHDtFhfc3V7Oxq6NWrFwC4XUjZKT093fW4U0JCAu69915s3LgRP/30EyoqKvD000+7rXPBBRfgiSeewK5du/DOO+8gLS1NXPTUx3lh38LCQtHznRcvdj5f0zQMHjwYAOo0wMjOzgag71kEgGHDhtW7nnNd53q+wELKhBr6gd+rF+Dc67tjByD87Pot/kcpw9xkmJscs5NhbjLMrRV17AgkJACe7n1RFP15zWxU4A1hYWF48cUXsWDBAkydOtVn79scw4cPR5cuXfDSSy+5HYL32WefYd++fZgyZQoA4MyZM3W6UCYkJCA8PNz1vIKCgjpF3JAhQwDA7bWb2/781KlT9a7z6quvusbudObMGezfvx8nT550LTt58mS9RWV9z3ceblmzoYeqqnjjjTcQHR3tKqD69++PwYMHY+3atW7vtXHjRmRlZWHixImNzsmbLNW1j/S9Uunp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",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 在 AdvertiseGen 数据集10K examples上使用 QLoRA 微调 ChatGLM3-6B，观察 Loss 变化情况\n",
    "# 定义全局变量和参数\n",
    "model_name_or_path = 'THUDM/chatglm3-6b'  # 模型ID或本地路径\n",
    "train_data_path = 'HasturOfficial/adgen'    # 训练数据路径\n",
    "eval_data_path = None                     # 验证数据路径，如果没有则设置为None\n",
    "seed = 8                                 # 随机种子\n",
    "max_input_length = 512                    # 输入的最大长度\n",
    "max_output_length = 1536                  # 输出的最大长度\n",
    "lora_rank = 4                             # LoRA秩\n",
    "lora_alpha = 32                           # LoRA alpha值\n",
    "lora_dropout = 0.05                       # LoRA Dropout率\n",
    "resume_from_checkpoint = None             # 如果从checkpoint恢复训练，指定路径\n",
    "prompt_text = ''                          # 所有数据前的指令文本\n",
    "compute_dtype = 'fp32'                    # 计算数据类型（fp32, fp16, bf16）\n",
    "\n",
    "from datasets import load_dataset\n",
    "from datasets import ClassLabel, Sequence\n",
    "import random\n",
    "import pandas as pd\n",
    "from IPython.display import display, HTML\n",
    "import torch\n",
    "from typing import List, Dict, Optional\n",
    "from transformers import AutoTokenizer, AutoModel, BitsAndBytesConfig, TrainingArguments, Trainer\n",
    "from peft import TaskType, LoraConfig, get_peft_model, prepare_model_for_kbit_training\n",
    "from peft.utils import TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING\n",
    "target_modules = TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING['chatglm']\n",
    "\n",
    "dataset = load_dataset(train_data_path)\n",
    "print(dataset) # 打印数据集结果\n",
    "dataset[\"train\"] = dataset[\"train\"].select(range(10000)) # 训练集只取10K的数据去训练\n",
    "\n",
    "\n",
    "def show_random_elements(dataset, num_examples=10):\n",
    "    assert num_examples <= len(dataset), \"Can't pick more elements than there are in the dataset.\"\n",
    "    picks = []\n",
    "    for _ in range(num_examples):\n",
    "        pick = random.randint(0, len(dataset)-1)\n",
    "        while pick in picks:\n",
    "            pick = random.randint(0, len(dataset)-1)\n",
    "        picks.append(pick)\n",
    "    \n",
    "    df = pd.DataFrame(dataset[picks])\n",
    "    for column, typ in dataset.features.items():\n",
    "        if isinstance(typ, ClassLabel):\n",
    "            df[column] = df[column].transform(lambda i: typ.names[i])\n",
    "        elif isinstance(typ, Sequence) and isinstance(typ.feature, ClassLabel):\n",
    "            df[column] = df[column].transform(lambda x: [typ.feature.names[i] for i in x])\n",
    "    display(HTML(df.to_html()))\n",
    "\n",
    "\n",
    "show_random_elements(dataset[\"train\"], num_examples=3)\n",
    "\n",
    "\n",
    "\n",
    "# revision='b098244' 版本对应的 ChatGLM3-6B 设置 use_reentrant=False\n",
    "# 最新版本 use_reentrant 被设置为 True，会增加不必要的显存开销\n",
    "tokenizer = AutoTokenizer.from_pretrained(model_name_or_path,\n",
    "                                          trust_remote_code=True,\n",
    "                                          use_reentrant=False,  # 推荐设置\n",
    "                                          revision='b098244')\n",
    "\n",
    "# tokenize_func 函数\n",
    "def tokenize_func(example, tokenizer, ignore_label_id=-100):\n",
    "    \"\"\"\n",
    "    对单个数据样本进行tokenize处理。\n",
    "    参数:\n",
    "    example (dict): 包含'content'和'summary'键的字典，代表训练数据的一个样本。\n",
    "    tokenizer (transformers.PreTrainedTokenizer): 用于tokenize文本的tokenizer。\n",
    "    ignore_label_id (int, optional): 在label中用于填充的忽略ID，默认为-100。\n",
    "    返回:\n",
    "    dict: 包含'tokenized_input_ids'和'labels'的字典，用于模型训练。\n",
    "    \"\"\"\n",
    "    # 构建问题文本\n",
    "    question = prompt_text + example['content']\n",
    "    if example.get('input', None) and example['input'].strip():\n",
    "        question += f'\\n{example[\"input\"]}'\n",
    "\n",
    "    # 构建答案文本\n",
    "    answer = example['summary']\n",
    "\n",
    "    # 对问题和答案文本进行tokenize处理 - 使用更基础的方法避免padding问题\n",
    "    # 注意：这里我们直接使用tokenizer的convert_tokens_to_ids方法\n",
    "    q_tokens = tokenizer.tokenize(question)\n",
    "    a_tokens = tokenizer.tokenize(answer)\n",
    "    \n",
    "    # 转换为ID\n",
    "    q_ids = tokenizer.convert_tokens_to_ids(q_tokens)\n",
    "    a_ids = tokenizer.convert_tokens_to_ids(a_tokens)\n",
    "\n",
    "    # 如果tokenize后的长度超过最大长度限制，则进行截断\n",
    "    if len(q_ids) > max_input_length - 2:  # 保留空间给gmask和bos标记\n",
    "        q_ids = q_ids[:max_input_length - 2]\n",
    "    if len(a_ids) > max_output_length - 1:  # 保留空间给eos标记\n",
    "        a_ids = a_ids[:max_output_length - 1]\n",
    "\n",
    "    # 构建模型的输入格式\n",
    "    input_ids = tokenizer.build_inputs_with_special_tokens(q_ids, a_ids)\n",
    "    question_length = len(q_ids) + 2  # 加上gmask和bos标记\n",
    "\n",
    "    # 构建标签，对于问题部分的输入使用ignore_label_id进行填充\n",
    "    labels = [ignore_label_id] * question_length + input_ids[question_length:]\n",
    "\n",
    "    return {'input_ids': input_ids, 'labels': labels}\n",
    "\n",
    "column_names = dataset['train'].column_names\n",
    "tokenized_dataset = dataset['train'].map(\n",
    "    lambda example: tokenize_func(example, tokenizer),\n",
    "    batched=False, \n",
    "    remove_columns=column_names\n",
    ")\n",
    "\n",
    "\n",
    "show_random_elements(tokenized_dataset, num_examples=1)\n",
    "\n",
    "tokenized_dataset = tokenized_dataset.shuffle(seed=seed)\n",
    "tokenized_dataset = tokenized_dataset.flatten_indices()\n",
    "\n",
    "\n",
    "\n",
    "# DataCollatorForChatGLM 类\n",
    "class DataCollatorForChatGLM:\n",
    "    \"\"\"\n",
    "    用于处理批量数据的DataCollator，尤其是在使用 ChatGLM 模型时。\n",
    "\n",
    "    该类负责将多个数据样本（tokenized input）合并为一个批量，并在必要时进行填充(padding)。\n",
    "\n",
    "    属性:\n",
    "    pad_token_id (int): 用于填充(padding)的token ID。\n",
    "    max_length (int): 单个批量数据的最大长度限制。\n",
    "    ignore_label_id (int): 在标签中用于填充的ID。\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, pad_token_id: int, max_length: int = 2048, ignore_label_id: int = -100):\n",
    "        \"\"\"\n",
    "        初始化DataCollator。\n",
    "\n",
    "        参数:\n",
    "        pad_token_id (int): 用于填充(padding)的token ID。\n",
    "        max_length (int): 单个批量数据的最大长度限制。\n",
    "        ignore_label_id (int): 在标签中用于填充的ID，默认为-100。\n",
    "        \"\"\"\n",
    "        self.pad_token_id = pad_token_id\n",
    "        self.ignore_label_id = ignore_label_id\n",
    "        self.max_length = max_length\n",
    "\n",
    "    def __call__(self, batch_data: List[Dict[str, List]]) -> Dict[str, torch.Tensor]:\n",
    "        \"\"\"\n",
    "        处理批量数据。\n",
    "\n",
    "        参数:\n",
    "        batch_data (List[Dict[str, List]]): 包含多个样本的字典列表。\n",
    "\n",
    "        返回:\n",
    "        Dict[str, torch.Tensor]: 包含处理后的批量数据的字典。\n",
    "        \"\"\"\n",
    "        # 计算批量中每个样本的长度\n",
    "        len_list = [len(d['input_ids']) for d in batch_data]\n",
    "        batch_max_len = max(len_list)  # 找到最长的样本长度\n",
    "\n",
    "        input_ids, labels = [], []\n",
    "        for len_of_d, d in sorted(zip(len_list, batch_data), key=lambda x: -x[0]):\n",
    "            pad_len = batch_max_len - len_of_d  # 计算需要填充的长度\n",
    "            # 添加填充，并确保数据长度不超过最大长度限制\n",
    "            ids = d['input_ids'] + [self.pad_token_id] * pad_len\n",
    "            label = d['labels'] + [self.ignore_label_id] * pad_len\n",
    "            if batch_max_len > self.max_length:\n",
    "                ids = ids[:self.max_length]\n",
    "                label = label[:self.max_length]\n",
    "            input_ids.append(torch.LongTensor(ids))\n",
    "            labels.append(torch.LongTensor(label))\n",
    "\n",
    "        # 将处理后的数据堆叠成一个tensor\n",
    "        input_ids = torch.stack(input_ids)\n",
    "        labels = torch.stack(labels)\n",
    "\n",
    "        return {'input_ids': input_ids, 'labels': labels, 'attention_mask': (input_ids != self.pad_token_id).long()}\n",
    "\n",
    "\n",
    "        \n",
    "# 准备数据整理器\n",
    "data_collator = DataCollatorForChatGLM(pad_token_id=tokenizer.pad_token_id)\n",
    "\n",
    "\n",
    "\n",
    "_compute_dtype_map = {\n",
    "    'fp32': torch.float32,\n",
    "    'fp16': torch.float16,\n",
    "    'bf16': torch.bfloat16\n",
    "}\n",
    "\n",
    "# QLoRA 量化配置\n",
    "q_config = BitsAndBytesConfig(load_in_4bit=True,\n",
    "                              bnb_4bit_quant_type='nf4',\n",
    "                              bnb_4bit_use_double_quant=True,\n",
    "                              bnb_4bit_compute_dtype=_compute_dtype_map['bf16'],\n",
    "                              llm_int8_enable_fp32_cpu_offload=True  # 关键参数\n",
    "                             )\n",
    "\n",
    "\n",
    "\n",
    "# revision='b098244' 版本对应的 ChatGLM3-6B 设置 use_reentrant=False\n",
    "# 最新版本 use_reentrant 被设置为 True，会增加不必要的显存开销\n",
    "model = AutoModel.from_pretrained(model_name_or_path,\n",
    "                                  quantization_config=q_config,\n",
    "                                  device_map='auto',\n",
    "                                  use_cache=False,  # 训练时关闭（梯度检查点需要重新计算中间结果，而缓存会阻止这一过程）\n",
    "                                  trust_remote_code=True,\n",
    "                                  revision='b098244',\n",
    "                                  max_memory={0: \"7GiB\"}\n",
    "                                 )\n",
    "\n",
    "# 确保使用新版检查点\n",
    "model.gradient_checkpointing_enable()\n",
    "\n",
    "# 获取当前模型占用的 GPU显存（差值为预留给 PyTorch 的显存）\n",
    "memory_footprint_bytes = model.get_memory_footprint()\n",
    "memory_footprint_mib = memory_footprint_bytes / (1024 ** 2)  # 转换为 MiB\n",
    "print(\"当前模型占用显存（单位为MiB）：\", memory_footprint_mib)\n",
    "\n",
    "print(f\"{memory_footprint_mib:.2f}MiB\")\n",
    "\n",
    "# 修补模型类（关键修复）\n",
    "original_class = model.__class__\n",
    "class PatchedChatGLM(original_class):\n",
    "    def _extract_past_from_model_output(self, output, *args, **kwargs):\n",
    "        # ChatGLM 特定的缓存提取逻辑\n",
    "        if hasattr(output, 'past_key_values'):\n",
    "            return output.past_key_values\n",
    "        return None\n",
    "\n",
    "model.__class__ = PatchedChatGLM\n",
    "\n",
    "# 准备量化（简化版）\n",
    "kbit_model = prepare_model_for_kbit_training(model)\n",
    "\n",
    "lora_config = LoraConfig(\n",
    "    target_modules=target_modules,\n",
    "    r=lora_rank,\n",
    "    lora_alpha=lora_alpha,\n",
    "    lora_dropout=lora_dropout,\n",
    "    bias='none',\n",
    "    inference_mode=False,\n",
    "    task_type=TaskType.CAUSAL_LM\n",
    ")\n",
    "\n",
    "qlora_model = get_peft_model(kbit_model, lora_config)  # 添加Lora\n",
    "\n",
    "qlora_model.print_trainable_parameters()\n",
    "\n",
    "# 定义训练参数（演示用，减少训练时间）\n",
    "training_demo_args = TrainingArguments(\n",
    "    output_dir=f\"models/demo/{model_name_or_path}\",  # 输出目录：保存模型和日志的路径\n",
    "    per_device_train_batch_size=16,                 # 每个GPU的训练batch大小：根据显存调整\n",
    "    gradient_accumulation_steps=4,                  # 梯度累积步数：模拟更大的batch size\n",
    "    learning_rate=1e-3,                             # 初始学习率：常用范围1e-5到1e-3\n",
    "    max_steps=100,                                  # 最大训练步数：控制演示时长\n",
    "    lr_scheduler_type=\"linear\",                     # 学习率调度策略：线性衰减\n",
    "    warmup_ratio=0.1,                               # 预热比例：前10%步数用于学习率预热\n",
    "    logging_steps=10,                               # 每10步记录一次日志\n",
    "    save_strategy=\"steps\",                          # 保存策略：按步数保存\n",
    "    save_steps=20,                                  # 每20步保存一次模型\n",
    "    optim=\"adamw_torch\",                            # 优化器：AdamW with torch实现\n",
    "    fp16=True,                                      # 启用混合精度训练：减少显存占用\n",
    "    report_to=[\"tensorboard\"],                      # 添加：记录指标到TensorBoard\n",
    "    gradient_checkpointing_kwargs={\"use_reentrant\": False},  # 显式设置\n",
    ")\n",
    "\n",
    "# 初始化Trainer\n",
    "trainer = Trainer(\n",
    "    model=qlora_model,                              # 要训练的模型（QLoRA适配后的模型）\n",
    "    args=training_demo_args,                        # 训练参数配置\n",
    "    train_dataset=tokenized_dataset,                # 训练数据集（已tokenize）\n",
    "    data_collator=data_collator                    # 数据整理器：处理padding和batch\n",
    ")\n",
    "\n",
    "assert model.config.use_cache is False, \"use_cache未正确关闭!\"\n",
    "# 开始训练并记录损失\n",
    "print(\"开始训练...\")\n",
    "train_result = trainer.train()                      # 返回的训练结果包含指标数据\n",
    "\n",
    "# 保存训练后的模型\n",
    "trainer.model.save_pretrained(f\"models/demo/{model_name_or_path}\")\n",
    "print(\"模型保存完成！\")\n",
    "\n",
    "# --- 新增：绘制训练损失曲线 ---\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.ticker import MaxNLocator\n",
    "\n",
    "# 从训练记录中提取损失值\n",
    "log_history = trainer.state.log_history\n",
    "train_loss = [log['loss'] for log in log_history if 'loss' in log]\n",
    "steps = [log['step'] for log in log_history if 'loss' in log]\n",
    "\n",
    "# 创建图表\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.plot(steps, train_loss, 'b-', linewidth=2, label='Training Loss')\n",
    "plt.title('Training Loss Curve', fontsize=14)\n",
    "plt.xlabel('Training Steps', fontsize=12)\n",
    "plt.ylabel('Loss', fontsize=12)\n",
    "plt.grid(True, linestyle='--', alpha=0.6)\n",
    "plt.gca().xaxis.set_major_locator(MaxNLocator(integer=True))  # X轴只显示整数值\n",
    "\n",
    "# 标记最低损失点\n",
    "min_loss_idx = train_loss.index(min(train_loss))\n",
    "plt.scatter(steps[min_loss_idx], train_loss[min_loss_idx], \n",
    "            c='red', s=100, label=f'Min Loss: {train_loss[min_loss_idx]:.3f}')\n",
    "plt.legend(fontsize=12)\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "fe73361c-9511-42e9-8f8d-3f2d52f9468f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "532828502703445a86f24cefacdc6043",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Loading checkpoint shards:   0%|          | 0/7 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "ChatGLM3-6B 微调前:\n",
      "连衣裙采用简约圆领设计，修饰颈部线条，凸显优雅气质。撞色印花图案点缀，增添文艺气息，凸显时尚感。压褶裙摆，修饰臀部曲线，显瘦显高。\n",
      "\n",
      "ChatGLM3-6B 微调后:\n",
      "连衣裙采用简约圆领设计，修饰颈部线条，凸显优雅气质。撞色印花图案点缀，增添文艺气息，凸显时尚感。压褶裙摆，修饰臀部曲线，显瘦显高。\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig\n",
    "from peft import PeftModel, PeftConfig\n",
    "\n",
    "# 1. 基础配置\n",
    "model_name_or_path = 'THUDM/chatglm3-6b'\n",
    "peft_model_path = 'models/demo/THUDM/chatglm3-6b'\n",
    "compute_dtype = torch.bfloat16\n",
    "\n",
    "# 2. 量化配置\n",
    "q_config = BitsAndBytesConfig(\n",
    "    load_in_4bit=True,\n",
    "    bnb_4bit_quant_type=\"nf4\",\n",
    "    bnb_4bit_use_double_quant=True,\n",
    "    bnb_4bit_compute_dtype=compute_dtype,\n",
    ")\n",
    "\n",
    "# 3. 加载基础模型\n",
    "base_model = AutoModelForCausalLM.from_pretrained(\n",
    "    model_name_or_path,\n",
    "    quantization_config=q_config,\n",
    "    trust_remote_code=True,\n",
    "    device_map=\"auto\",\n",
    "    use_cache=True,\n",
    "    torch_dtype=compute_dtype,\n",
    "    revision='b098244'\n",
    ")\n",
    "base_model.requires_grad_(False)\n",
    "base_model.eval()\n",
    "\n",
    "# 4. 加载tokenizer但不使用其预处理功能\n",
    "tokenizer = AutoTokenizer.from_pretrained(\n",
    "    model_name_or_path,\n",
    "    trust_remote_code=True,\n",
    "    revision='b098244'\n",
    ")\n",
    "\n",
    "# 手动设置关键token ID（ChatGLM3-6B特定值）\n",
    "BOS_TOKEN_ID = 64792  # sop\n",
    "EOS_TOKEN_ID = 2      # </s>\n",
    "PAD_TOKEN_ID = 0      # <unk>\n",
    "\n",
    "# 5. 加载LoRA适配器\n",
    "peft_config = PeftConfig.from_pretrained(peft_model_path)\n",
    "peft_config.inference_mode = True\n",
    "\n",
    "model = PeftModel.from_pretrained(\n",
    "    base_model,\n",
    "    peft_model_path,\n",
    "    config=peft_config,\n",
    "    torch_dtype=compute_dtype\n",
    ")\n",
    "model.eval()\n",
    "\n",
    "# 6. 完全手动实现的生成函数\n",
    "def generate_text(curModel, input_text, max_new_tokens=100):\n",
    "    \"\"\"完全绕过tokenizer预处理的生成函数\"\"\"\n",
    "    try:\n",
    "        # 步骤1：手动tokenization\n",
    "        tokens = tokenizer.tokenize(input_text)\n",
    "        \n",
    "        # 步骤2：转换为token IDs\n",
    "        input_ids = tokenizer.convert_tokens_to_ids(tokens)\n",
    "        \n",
    "        # 步骤3：添加特殊token\n",
    "        input_ids = [BOS_TOKEN_ID] + input_ids + [EOS_TOKEN_ID]\n",
    "        \n",
    "        # 步骤4：创建并准备输入tensor\n",
    "        input_tensor = torch.tensor([input_ids], dtype=torch.long, device=model.device)\n",
    "        # print(\"input_tensor-size: \", input_tensor.shape)\n",
    "        \n",
    "        # 步骤5：生成（手动实现生成逻辑）\n",
    "        generated_tokens = []\n",
    "        with torch.no_grad():\n",
    "            for _ in range(max_new_tokens):\n",
    "                outputs = curModel(input_ids=input_tensor)\n",
    "                next_token_logits = outputs.logits[:, -1, :]\n",
    "                next_token = torch.argmax(next_token_logits, dim=-1)\n",
    "                generated_tokens.append(next_token.item())\n",
    "                \n",
    "                # 修正维度不匹配问题\n",
    "                next_token = next_token.unsqueeze(0)  # 添加batch维度\n",
    "                input_tensor = torch.cat([input_tensor, next_token], dim=1)\n",
    "                \n",
    "                # print(\"当前input_tensor-size: \", input_tensor.shape)\n",
    "                if next_token.item() == EOS_TOKEN_ID:\n",
    "                    break\n",
    "        \n",
    "        # 步骤6：解码结果（跳过特殊token）\n",
    "        generated_text = tokenizer.decode(generated_tokens, skip_special_tokens=True)\n",
    "        return generated_text\n",
    "    except Exception as e:\n",
    "        print(f\"生成失败: {str(e)}\")\n",
    "        return \"\"\n",
    "\n",
    "# 7. 测试\n",
    "if __name__ == \"__main__\":\n",
    "    test_input = \"类型#裙*版型#显瘦*风格#文艺*风格#简约*图案#印花*图案#撞色*裙下摆#压褶*裙长#连衣裙*裙领型#圆领\"\n",
    "    \n",
    "    # print(\"=== 生成测试 ===\")\n",
    "    # print(\"关键token ID检查:\")\n",
    "    # print(f\"BOS: {BOS_TOKEN_ID}\")\n",
    "    # print(f\"EOS: {EOS_TOKEN_ID}\")\n",
    "    # print(f\"PAD: {PAD_TOKEN_ID}\")\n",
    "\n",
    "    result = generate_text(base_model, test_input)\n",
    "    print(\"\\nChatGLM3-6B 微调前:\")\n",
    "    print(result)\n",
    "    \n",
    "    result = generate_text(model, test_input)\n",
    "    print(\"\\nChatGLM3-6B 微调后:\")\n",
    "    print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "220ec0c7-fad5-4783-b9b8-f59dfb37732c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
